<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>异常客流检测 - 哈尔滨智慧公交</title>
    <link rel="stylesheet" href="css/style.css?v=1.1">
</head>

<body>
    <div class="navbar">
        <a href="index.html">首页</a>
        <a href="traffic_trend.html">客流趋势分析</a>
        <a href="station_ranking.html">站点客流排行</a>
        <a href="funnel_analysis.html">线路站点客流分析</a>
        <a href="line_comparison.html">线路客流量对比</a>
        <a href="transfer_heatmap.html">换乘热度分析</a>
        <a href="poi_analysis.html">站点周边客流特征</a>
        <a href="anomaly_detection.html">异常客流检测</a>
        <a href="od_analysis.html">路径OD分析</a>
        <a href="stop_frequency_usage_analysis.html">站停频率分析</a>
        <a href="dispatch_suggestion.html">智能调度模型</a>
    </div>
    <div class="page-container">
        <div class="chart-header">
            <h2 class="chart-title">异常客流模式检测</h2>
            <p class="chart-description">
                此折线图展示了客流数据的时间序列，并通过标记点识别出非典型的客流模式。异常检测有助于发现突然的客流高峰或低谷，这可能指示着特殊活动、突发事件或数据异常，交通管理部门可以据此做出快速响应和调整。
            </p>
        </div>
        <div class="kpi-cards-container">
            <div class="kpi-card">
                <h4>异常高点总数</h4>
                <p id="kpi-high-anomalies">--</p>
            </div>
            <div class="kpi-card">
                <h4>异常低点总数</h4>
                <p id="kpi-low-anomalies">--</p>
            </div>
            <div class="kpi-card">
                <h4>最高客流峰值</h4>
                <p id="kpi-max-peak">--</p>
            </div>
            <div class="kpi-card">
                <h4>最低客流谷值</h4>
                <p id="kpi-min-dip">--</p>
            </div>
        </div>
        <div id="anomaly-detection-chart" class="chart-container"></div>
    </div>
    <script src="js/echarts.min.js"></script>
    <script type="text/javascript">
        document.addEventListener('DOMContentLoaded', function () {
            var anomalyDetectionChart = echarts.init(document.getElementById('anomaly-detection-chart'));

            fetch('/api/traffic/hourly_trends')
                .then(response => response.json())
                .then(data => {
                    var hours = data.map(item => item.hour + ':00');
                    var passengerFlow = data.map(item => item.onboard + item.offboard);

                    // --- 最终方案：智能标记异常区间的峰值 ---
                    const MOVING_AVG_WINDOW = 2;
                    const ANOMALY_RELATIVE_THRESHOLD = 0.50;
                    const ANOMALY_ABSOLUTE_THRESHOLD = 5000;

                    const calculateAverage = (arr) => {
                        if (arr.length === 0) return 0;
                        return arr.reduce((a, b) => a + b, 0) / arr.length;
                    };

                    // 1. 初始标记所有异常点
                    let anomalies = passengerFlow.map((value, index) => {
                        const neighbors = [];
                        for (let i = 1; i <= MOVING_AVG_WINDOW; i++) {
                            if (index - i >= 0) neighbors.push(passengerFlow[index - i]);
                            if (index + i < passengerFlow.length) neighbors.push(passengerFlow[index + i]);
                        }
                        const localAverage = calculateAverage(neighbors);
                        const deviation = value - localAverage;
                        const relativeDeviation = localAverage > 0 ? deviation / localAverage : 0;
                        const isSignificant = Math.abs(deviation) > ANOMALY_ABSOLUTE_THRESHOLD && Math.abs(relativeDeviation) > ANOMALY_RELATIVE_THRESHOLD;

                        if (isSignificant) {
                            return { index: index, value: value, type: deviation > 0 ? 'high' : 'low' };
                        }
                        return null;
                    }).filter(Boolean);

                    // 2. 将连续的异常点分组 (聚类)
                    let clusters = [];
                    if (anomalies.length > 0) {
                        let currentCluster = [anomalies[0]];
                        for (let i = 1; i < anomalies.length; i++) {
                            if (anomalies[i].index === anomalies[i - 1].index + 1 && anomalies[i].type === anomalies[i - 1].type) {
                                currentCluster.push(anomalies[i]);
                            } else {
                                clusters.push(currentCluster);
                                currentCluster = [anomalies[i]];
                            }
                        }
                        clusters.push(currentCluster);
                    }

                    // 3. 计算并更新KPI卡片
                    const peakAnomalies = clusters.filter(c => c.length > 0 && c[0].type === 'high');
                    const dipAnomalies = clusters.filter(c => c.length > 0 && c[0].type === 'low');

                    document.getElementById('kpi-high-anomalies').textContent = peakAnomalies.length;
                    document.getElementById('kpi-low-anomalies').textContent = dipAnomalies.length;
                    document.getElementById('kpi-max-peak').textContent = `${Math.max(...passengerFlow).toLocaleString()} 人次`;
                    document.getElementById('kpi-min-dip').textContent = `${Math.min(...passengerFlow).toLocaleString()} 人次`;

                    // 4. 准备最终的 markedData
                    var markedData = passengerFlow.slice();
                    clusters.forEach(cluster => {
                        if (cluster.length === 0) return;

                        let peakPoint = cluster[0];
                        if (cluster[0].type === 'high') {
                            peakPoint = cluster.reduce((max, p) => p.value > max.value ? p : max, cluster[0]);
                        } else {
                            peakPoint = cluster.reduce((min, p) => p.value < min.value ? p : min, cluster[0]);
                        }

                        cluster.forEach(point => {
                            const isPeak = (point.index === peakPoint.index);
                            const type = point.type;

                            markedData[point.index] = {
                                value: point.value,
                                symbol: type === 'high' ? 'circle' : 'diamond',
                                symbolSize: isPeak ? 15 : 10, // 峰值点大，其他点小
                                itemStyle: { color: type === 'high' ? 'red' : 'green' },
                                label: {
                                    show: isPeak, // 只在峰值点显示标签
                                    formatter: type === 'high' ? '突发高点: {c}' : '意外低点: {c}',
                                    position: type === 'high' ? 'top' : 'bottom',
                                    color: type === 'high' ? 'red' : 'green',
                                    fontSize: 14
                                }
                            };
                        });
                    });

                    var option = {
                        title: {
                            text: '异常客流模式检测',
                            subtext: '数据驱动的异常点识别',
                            left: 'center'
                        },
                        tooltip: {
                            trigger: 'axis'
                        },
                        xAxis: {
                            type: 'category',
                            data: hours,
                            name: '时间'
                        },
                        yAxis: {
                            type: 'value',
                            name: '客流量 (人次)'
                        },
                        series: [{
                            data: markedData,
                            type: 'line',
                            smooth: true,
                        }]
                    };
                    anomalyDetectionChart.setOption(option);
                })
                .catch(error => {
                    console.error('Error fetching anomaly detection data:', error);
                    document.getElementById('anomaly-detection-chart').innerText = '数据加载失败，请检查后端服务是否正常。';
                });

            window.addEventListener('resize', function () {
                if (anomalyDetectionChart) {
                    anomalyDetectionChart.resize();
                }
            });
        });
    </script>
    <script src="js/common.js"></script>
</body>

</html>